/*
	Written by Daniel Mathis
	Defines the game object hierarchy, and fleshes out each class's functions and data members.
*/

#include <list>
#include <GL/gl.h>
#include <GL/glu.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <vector>

#define SHIPZ 1.0
#define ASTEROIDZ 0.0
#define BULLETZ 0.0
#define SHIPACC 0.0011
#define BULLETACC 0.0011
#define ASTEROIDACC 0.0011
#define ASTEROIDMINVEL 0.01
#define ASTEROIDMAXVEL 0.1
#define ASTEROIDMINWIDTH 15
#define ASTEROIDMINHEIGHT 15
#define BULLETWIDTH 5
#define BULLETHEIGHT 5
#define BULLETVEL 0.4
#define BULLETLIFETIME 1200

#define TURNAMOUNT 0.01
#define WIDTH 640
#define HEIGHT 480

#ifndef _GameObjects_h_
#define _GameObjects_h_

using namespace std;

class GameObject {
	public:
		virtual void draw() = 0;
		virtual GLfloat itsWidth() const = 0;
		virtual GLfloat itsHeight() const = 0;
		virtual GLfloat itsXLoc() const = 0;
		virtual GLfloat itsYLoc() const = 0;
		virtual GLfloat itsRadius() const = 0;
		virtual GLfloat itsDirection() const = 0;
		virtual void move() = 0;
		virtual void accelerate(bool val) = 0;
		virtual void decelerate(bool val) = 0;
		virtual ~GameObject() {};
};

class Ship: public GameObject {
	public:
		Ship(const GLfloat newWidth, const GLfloat newHeight, GLfloat newXLoc, GLfloat newYLoc);
		void draw();
		GLfloat itsWidth() const { return width; }
		GLfloat itsHeight() const { return height; }
		GLfloat itsXLoc() const { return xLoc; }
		GLfloat itsYLoc() const { return yLoc; }
		GLfloat itsRadius() const { return radius; }
		GLfloat itsDirection() const { return direction; }
		void move();
		void accelerate(bool val) { accelerating = val; }
		void decelerate(bool val) { decelerating = val; }
		void turnLeft(bool val) { turningLeft = val; }
		void turnRight(bool val) { turningRight = val; }
	private:
		const GLfloat width;
		const GLfloat height;
		const GLfloat radius;  // Used for bounding circle
		GLfloat xLoc;
		GLfloat yLoc;
		GLfloat xVelocity;
		GLfloat yVelocity;
		bool accelerating;
		bool decelerating;
		bool turningLeft;
		bool turningRight;
		GLfloat direction; // Between 0 and 6.2831 -- Radians
};

class ShipController{
	public:
		ShipController() {}
		void generateShip(const GLfloat newWidth, const GLfloat newHeight, GLfloat newXLoc, GLfloat newYLoc);
		void deleteShip();
		void draw();
		void move();
		Ship* getShip() { return shipObjects[0]; }
	private:
		vector<Ship*> shipObjects;
};

class Asteroid: public GameObject {
	public:
		Asteroid(const GLfloat newWidth, const GLfloat newHeight, const GLfloat newXLoc, const GLfloat newYLoc, const GLfloat initXVelocity, const GLfloat initYVelocity);
		void draw();
		GLfloat itsWidth() const { return width; }
		GLfloat itsHeight() const { return height; }
		GLfloat itsXLoc() const { return xLoc; }
		GLfloat itsYLoc() const { return yLoc; }
		GLfloat itsRadius() const { return radius; }
		GLfloat itsDirection() const { return direction; }
		void move();
		void accelerate(bool val) { accelerating = val; }
		void decelerate(bool val) { decelerating = val; }
	private:
		const GLfloat width;
		const GLfloat height;
		const GLfloat radius; // Used for bounding circle
		GLfloat xLoc;
		GLfloat yLoc;
		GLfloat xVelocity;
		GLfloat yVelocity;
		bool accelerating;
		bool decelerating;
		GLfloat direction; // Between 0 and 6.2831 -- Radians
};

class AsteroidController {
	public:
		AsteroidController() {}
		void generateAsteroids(const int numberOfAsteroids, GLfloat maxWidth=20, GLfloat maxHeight=20, const GLfloat minVelocity=ASTEROIDMINVEL, const GLfloat maxVelocity=ASTEROIDMAXVEL);
		void deleteAsteroids();
		void draw();
		void move();
		unsigned int numberOfAsteroids() { return asteroidObjects.size(); }
		list<Asteroid*>::iterator getAsteroidIterator() { return asteroidObjects.begin(); }
		list<Asteroid*>::iterator getAsteroidIteratorEnd() { return asteroidObjects.end(); }
	private:
		list<Asteroid*> asteroidObjects;
};

class Bullet: public GameObject {
	public:
		Bullet(const GLfloat newXLoc, const GLfloat newYLoc, const GLfloat newDirection, const GLfloat newWidth=BULLETWIDTH, const GLfloat newHeight=BULLETHEIGHT, const GLfloat initXVelocity=BULLETVEL, const GLfloat initYVelocity=BULLETVEL);
		void draw();
		GLfloat itsWidth() const { return width; }
		GLfloat itsHeight() const { return height; }
		GLfloat itsXLoc() const { return xLoc; }
		GLfloat itsYLoc() const { return yLoc; }
		GLfloat itsRadius() const { return radius; }
		GLfloat itsDirection() const { return direction; }
		void move();
		void accelerate(bool val) { accelerating = val; }
		void decelerate(bool val) { decelerating = val; }
		unsigned int itsLifeTime() const { return lifeTime; }
		
	private:
		const GLfloat width;
		const GLfloat height;
		const GLfloat radius; // Used for bounding circle
		GLfloat xLoc;
		GLfloat yLoc;
		GLfloat xVelocity;
		GLfloat yVelocity;
		bool accelerating;
		bool decelerating;
		GLfloat direction; // Between 0 and 6.2831 -- Radians
		unsigned int lifeTime; // Between 0 and BULLETLIFETIME -- increases on each move
};

class BulletController {
	public:
		BulletController(){}
		void generateBullet(const GLfloat newXLoc, const GLfloat newYLoc, const GLfloat newDirection, const GLfloat newWidth=BULLETWIDTH, const GLfloat newHeight=BULLETHEIGHT, const GLfloat initXVelocity=BULLETVEL, const GLfloat initYVelocity=BULLETVEL);
		void deleteBullets();
		void draw();
		void move();
		unsigned int numberOfBullets() { return bulletObjects.size(); }
		list<Bullet*>::iterator getBulletIterator() { return bulletObjects.begin(); }
		list<Bullet*>::iterator getBulletIteratorEnd() { return bulletObjects.end(); }
	private:
		list<Bullet*> bulletObjects;
};

#endif
